export default class Ctx2 {
  constructor(ctx, canW, canH) {
    this.ctx = ctx
    this.canW = canW
    this.canH = canH
    this.handle = []
    this.viewList = []
    this.timer = null
  }
  draw(j) {
    this.setHandle(j)
    j.forEach((v1) => {
      this.forDraw(v1)
    })
    return this
  }
  forDraw(v1) {
    if (v1.save) {
      this.save()
    }
    if (v1.rotate && v1.rotate.length) {
      this.rotate(...v1.rotate)
    }
    if (v1.clip) {
      this.clip()
    }
    if (v1.globalAlpha !== undefined && v1.globalAlpha !== null) {
      this.globalAlpha(v1.globalAlpha)
    }
    if (!v1.hide && v1.type) {
      v1.type.bind(this)(...v1.attr)
      if (v1.rotate && v1.rotate.length) {
      }
    }
    if (v1.restore) {
      this.restore()
    }
  }
  remove() {
    this.handle = []
    this.viewList = []
    if (this.timer) {
      clearInterval(this.timer)
    }
  }
  push(j) {
    this.viewList.push(...j)
    this.viewList.forEach((v1, index, arr) => {
      if (!v1.zIndex) {
        v1.zIndex = index
      }
      arr[index] = v1
    })
    return this
  }
  show(door) {
    if (!door) {
      if (this.timer) {
        clearInterval(this.timer)
      }
      this.timer = setInterval(() => {
        this.clearRect(0, 0, this.canW, this.canH)
        this.showList()
      }, 1000 / 60)
    } else {
      if (this.timer) {
        clearInterval(this.timer)
      }
    }
    return this
  }
  showList() {
    let temp1 = this.viewList.sort((a, b) => {
      return a.zIndex - b.zIndex
    })
    temp1.forEach((v1) => {
      if (v1.hide) return
      if (v1.handle) {
        v1.handle(v1)
      }
      this.forDraw(v1)
    })
    this.setHandle(temp1, true)
  }
  setHandle(i, j) {
    let fix = i.filter((v1) => {
      return v1.click || v1.mousedown || v1.mousemove || v1.mouseup || v1.touchstart || v1.touchmove || v1.touchend
    })
    if (j) {
      this.handle = [...fix].reverse()
    } else {
      this.handle = [...this.handle, ...fix]
    }
  }
  onEvent(can) {
    can.onclick = (ev) => {
      this.onclick(this.pointXY(can, ev), 'click')
    }

    can.onmousemove = (ev) => {
      ev.preventDefault()
      this.onclick(this.pointXY(can, ev), 'mousemove')
    }

    can.onmousedown = (ev) => {
      this.onclick(this.pointXY(can, ev), 'mousedown')
    }

    can.onmouseup = (ev) => {
      this.onclick(this.pointXY(can, ev), 'mouseup')
    }

    can.ontouchmove = (ev) => {
      ev.preventDefault()
      this.onclick(this.pointXY(can, ev, 1), 'touchmove')
    }

    can.ontouchstart = (ev) => {
      this.onclick(this.pointXY(can, ev, 1), 'touchstart')
    }

    can.ontouchend = (ev) => {
      this.onclick(this.pointXY(can, ev, 2), 'touchend')
    }
  }
  removeHandle() {
    this.handle = []
  }
  pointXY(can, ev, num) {
    var bbox = can.getBoundingClientRect()
    if (num === 1) {
      return {
        x: ev.targetTouches[0].clientX - bbox.left,
        y: ev.targetTouches[0].clientY - bbox.top,
      }
    } else if (num === 2) {
      return {
        x: ev.changedTouches[0].clientX - bbox.left,
        y: ev.changedTouches[0].clientY - bbox.top,
      }
    } else {
      return {
        x: ev.clientX - bbox.left,
        y: ev.clientY - bbox.top,
      }
    }
  }
  onclick(ev, type) {
    for (let i = 0; i < this.handle.length; i++) {
      let evItem = null
      let v1 = this.handle[i]
      if (v1.attr && v1.name) {
        if (v1.name === 'rectFill' || v1.name === 'imgFill') {
          if (this.innerRect(ev.x, ev.y, v1.attr[1], v1.attr[2], v1.attr[3], v1.attr[4])) {
            evItem = v1
          }
        } else if (v1.name === 'edgeFill') {
          if (this.innerEdge({ x: ev.x, y: ev.y }, v1.attr[0])) {
            evItem = v1
          }
        } else if (v1.name === 'arcStroke' || v1.name === 'arcFill') {
          if (this.innerArc({ x: ev.x, y: ev.y }, { x: v1.attr[0][0], y: v1.attr[0][1] }, v1.attr[0][2])) {
            evItem = v1
          }
        }
      }

      if (type === 'click' && v1.click) {
        v1.click(evItem, ev)
      }
      if (type === 'mousedown' && v1.mousedown) {
        v1.mousedown(evItem, ev)
      }
      if (type === 'mousemove' && v1.mousemove) {
        v1.mousemove(evItem, ev)
      }
      if (type === 'mouseup' && v1.mouseup) {
        v1.mouseup(evItem, ev)
      }
      if (type === 'touchstart' && v1.touchstart) {
        v1.touchstart(evItem, ev)
      }
      if (type === 'touchmove' && v1.touchmove) {
        v1.touchmove(evItem, ev)
      }
      if (type === 'touchend' && v1.touchend) {
        v1.touchend(evItem, ev)
      }

      if (evItem) {
        break
      }
    }
  }
  imgArcFill(src, x, y, w, h) {
    // 圆形图片
    this.save()
    try {
      if (src) {
        this.ctx.beginPath()
        this.ctx.arc(x + w / 2, y + h / 2, w / 2, 0, Math.PI * 2, false)
        this.ctx.clip()
        this.ctx.drawImage(src, x, y, w, h)
      }
    } catch (error) {}
    this.restore()
    return this
  }
  imgRadiusFill(src, x, y, w, h, r) {
    // 圆角图片
    this.save()
    try {
      if (src) {
        this.ctx.beginPath()
        this.ctx.moveTo(x + r, y)
        this.ctx.arcTo(x + w, y, x + w, y + h, r)
        this.ctx.arcTo(x + w, y + h, x, y + h, r)
        this.ctx.arcTo(x, y + h, x, y, r)
        this.ctx.arcTo(x, y, x + w, y, r)
        this.ctx.clip()
        this.ctx.drawImage(src, x, y, w, h)
      }
    } catch (error) {}
    this.restore()
    return this
  }
  arcFill(a = [], b) {
    // 圆 [Array, String, Array]
    this.ctx.beginPath()
    this.ctx.fillStyle = b
    this.ctx.moveTo(a[0], a[1])
    this.ctx.arc(a[0], a[1], a[2], a[3] || 0, a[4] || Math.PI * 2, false)
    this.ctx.fill()
    this.ctx.closePath()
    return this
  }
  edgeFill(a = [], b) {
    // 多边形 [Array, String]
    this.ctx.beginPath()
    this.ctx.fillStyle = b
    a.forEach((item, index) => {
      if (index === 0) {
        this.ctx.moveTo(item.x, item.y)
      } else {
        this.ctx.lineTo(item.x, item.y)
      }
    })
    this.ctx.fill()
    this.ctx.closePath()
    return this
  }
  clip() {
    this.ctx.clip()
    return this
  }
  globalAlpha(a = 1) {
    // 透明度 [Number]
    this.ctx.globalAlpha = a
    return this
  }
  lineStroke(a = [], b, c = 1, d = []) {
    // 线 [Array, String, Number]
    this.ctx.beginPath()
    this.ctx.lineWidth = c
    this.ctx.strokeStyle = b
    this.ctx.setLineDash(d)
    a.forEach((item, index) => {
      if (index === 0) {
        this.ctx.moveTo(item.x, item.y)
      } else {
        this.ctx.lineTo(item.x, item.y)
      }
    })
    this.ctx.stroke()
    this.ctx.closePath()
    return this
  }
  createLinearGradient(x, y, w, h, a) {
    // 线性的渐变颜色 [Number, Number, Number, Number, Array]
    let grd = this.ctx.createLinearGradient(x, y, w, h)
    a.forEach((item) => {
      grd.addColorStop(...item)
    })
    return grd
  }
  createRadialGradient(x, y, r, a) {
    // 圆形的渐变颜色 [Number, Number, Number, Array]
    let grd = this.ctx.createRadialGradient(x, y, 0, x, y, r)
    a.forEach((item) => {
      grd.addColorStop(...item)
    })
    return grd
  }
  arcStroke(a = [], b, c, d) {
    // 圆 [Array, String, Number, Array]
    this.ctx.beginPath()
    this.ctx.lineWidth = c
    this.ctx.strokeStyle = b
    if (d) {
      this.ctx.moveTo(...d)
    }
    this.ctx.arc(a[0], a[1], a[2], a[3] || 0, a[4] || Math.PI * 2)
    this.ctx.stroke()
    this.ctx.closePath()
    return this
  }
  textFill(a, b, c, d = []) {
    // 文字 [String, Number, Number, Array]
    this.ctx.fillStyle = d[0] || '#fff'
    this.ctx.font = d[1] || '16px bold'
    this.ctx.textAlign = d[2] || 'center'
    this.ctx.textBaseline = d[3] || 'middle'
    this.ctx.fillText(a, b, c)
    return this
  }
  rectStroke(a = [], b, c) {
    // 方 [Array, String, Number, Array]
    this.ctx.beginPath()
    this.ctx.lineWidth = c
    this.ctx.strokeStyle = b
    this.ctx.rect(a[0], a[1], a[2], a[3])
    this.ctx.stroke()
    this.ctx.closePath()
    return this
  }
  rectFill(a, x, y, w, h) {
    // 方行 [String, Number, Number, Number, Number]
    this.ctx.fillStyle = a
    this.ctx.fillRect(x, y, w, h)
    return this
  }
  imgFill(src, x, y, w, h, xywh) {
    // 图片
    try {
      if (src) {
        if (xywh) {
          this.ctx.drawImage(src, x, y, w, h, ...xywh)
        } else {
          this.ctx.drawImage(src, x, y, w, h)
        }
      }
    } catch (error) {}
    return this
  }
  penStroke(flag, x1, y1, a = 2, b = '#000') {
    // 画笔 [Number, Number, Number, Number, String]
    if (flag == 1) {
      this.ctx.beginPath()
      this.ctx.strokeStyle = b
      this.ctx.lineWidth = a
      this.ctx.moveTo(x1, y1)
    } else if (flag == 2) {
      this.ctx.lineTo(x1, y1)
      this.ctx.stroke()
    } else if (flag == 3) {
      this.ctx.closePath()
    }
    return this
  }
  rotate(x, y, r, s1, s2) {
    // 动作 [Number, Number, Number, Number, Number]
    this.ctx.translate(x, y)
    this.ctx.rotate((r * Math.PI) / 180)
    this.ctx.scale(s1, s2)
    this.ctx.translate(-x, -y)
    return this
  }
  scale(s1 = 1, s2 = 1) {
    this.ctx.scale(s1, s2)
    return this
  }
  globalCompositeOperation(i) {
    this.ctx.globalCompositeOperation = i
    return this
  }
  clearRect(x, y, w, h) {
    // 清除 [Number, Number, Number, Number]
    this.ctx.clearRect(x, y, w, h)
    return this
  }
  loadImg(src) {
    // 加载图片 [String]
    return new Promise((resolve, reject) => {
      let img = new Image()
      img.src = src
      img.onload = () => {
        resolve(img)
      }
      img.onerror = () => {
        reject()
      }
    })
  }
  save() {
    // 保存现在状态
    this.ctx.save()
    return this
  }
  restore() {
    // 恢复之前状态
    this.ctx.restore()
    return this
  }
  next(fn) {
    // 下一步
    fn()
    return this
  }
  getEmptyPx(width, height) {
    // 获取空像素百分比 [Number, Number]
    let imgData = this.ctx.getImageData(0, 0, width, height)
    let pixles = imgData.data
    let transPixs = []
    for (let i = 0, j = pixles.length; i < j; i += 4) {
      let a = pixles[i + 3]
      if (a < 128) {
        transPixs.push(i)
      }
    }
    return ((transPixs.length / (pixles.length / 4)) * 100).toFixed(2)
  }
  clearArcFun(x, y, r) {
    // 圆形清除 [Number, Number, Number]
    let stepClear = 1
    let _this = this
    clearArc(x, y, r)
    function clearArc(x, y, radius) {
      let calcWidth = radius - stepClear
      let calcHeight = Math.sqrt(radius * radius - calcWidth * calcWidth)

      let posX = x - calcWidth
      let posY = y - calcHeight

      let widthX = 2 * calcWidth
      let heightY = 2 * calcHeight

      if (stepClear <= radius) {
        _this.ctx.clearRect(posX, posY, widthX, heightY)
        stepClear += 1
        clearArc(x, y, radius)
      }
    }
  }
  innerEdge(dot, coordinates, noneZeroMode) {
    // [{x,y}, [{x,y}], number]
    // 默认启动none zero mode
    noneZeroMode = noneZeroMode || 1
    var x = dot.x,
      y = dot.y
    var crossNum = 0
    // 点在线段的左侧数目
    var leftCount = 0
    // 点在线段的右侧数目
    var rightCount = 0
    for (var i = 0; i < coordinates.length - 1; i++) {
      var start = coordinates[i]
      var end = coordinates[i + 1]

      // 起点、终点斜率不存在的情况
      if (start.x === end.x) {
        // 因为射线向右水平，此处说明不相交
        if (x > start.x) continue

        // 从左侧贯穿
        if (end.y > start.y && y >= start.y && y <= end.y) {
          leftCount++
          crossNum++
        }
        // 从右侧贯穿
        if (end.y < start.y && y >= end.y && y <= start.y) {
          rightCount++
          crossNum++
        }
        continue
      }
      // 斜率存在的情况，计算斜率
      var k = (end.y - start.y) / (end.x - start.x)
      // 交点的x坐标
      var x0 = (y - start.y) / k + start.x
      // 因为射线向右水平，此处说明不相交
      if (x > x0) continue

      if (end.x > start.x && x0 >= start.x && x0 <= end.x) {
        crossNum++
        if (k >= 0) leftCount++
        else rightCount++
      }
      if (end.x < start.x && x0 >= end.x && x0 <= start.x) {
        crossNum++
        if (k >= 0) rightCount++
        else leftCount++
      }
    }

    return noneZeroMode === 1 ? leftCount - rightCount !== 0 : crossNum % 2 === 1
  }
  pointsList(radius, num, moveX = 0, moveY = 0) {
    let points = []
    for (let i = 0; i < num; i++) {
      const angle = i * Math.PI * (2 / num)
      const x = radius * Math.cos(angle)
      const y = radius * Math.sin(angle)
      points.push({
        x: x + moveX,
        y: y + moveY,
      })
    }
    points.push(points[0])
    return points
  }
  innerArc(xy1, xy2, rr) {
    let xx = xy1.x - xy2.x
    let yy = xy1.y - xy2.y
    let distance = Math.pow(xx, 2) + Math.pow(yy, 2) - Math.pow(rr, 2)
    if (distance < 0) {
      return true
    } else {
      return false
    }
  }
  innerRect(x, y, minX, minY, minX1, minY1) {
    let maxX = minX + minX1
    let maxY = minY + minY1
    if (x >= minX && x <= maxX && y >= minY && y <= maxY) {
      return true
    } else {
      return false
    }
  }
  imgZip(image, imgW, imgH) {
    // 图片压缩
    let canvas = document.createElement('canvas')
    let ctx = canvas.getContext('2d')
    canvas.width = imgW
    canvas.height = imgH
    ctx.clearRect(0, 0, imgW, imgH)
    ctx.drawImage(image, 0, 0, imgW, imgH)
    let blobUrl = canvas.toDataURL('image/png')
    // canvas.toDataURL('image/jpeg', 0.7)
    return blobUrl
  }
  shot(can) {
    let dataStr = can.toDataURL('image/png')
    var a = document.createElement('a')
    document.body.appendChild(a)
    a.style = 'display: none'
    a.href = dataStr
    a.download = '截图'
    a.click()
    a.remove()
  }
  drawDom(dom, fn) {
    // 渲染dom
    // 中文支持自动换行, 不支持连续的英文数字，需要用空格
    let data = `
    <svg xmlns="http://www.w3.org/2000/svg">
      <foreignObject width="100%" height="100%">
        <body xmlns="http://www.w3.org/1999/xhtml">
          ${dom}
        </body>
      </foreignObject>
    </svg>
    `
    let img = new Image()
    let svg = new Blob([data], { type: 'image/svg+xml;charset=utf-8' })
    let url = window.URL.createObjectURL(svg)
    img.src = url
    img.onload = () => {
      fn(img)
      window.URL.revokeObjectURL(url)
    }
    img.onerror = () => {
      fn('')
      window.URL.revokeObjectURL(url)
    }
  }
}
